import geopandas as gpd
from networkx.generators import line
import shapely
import numpy as np

class MOVE_FUNCTION:
    func_linear = lambda x,distance,threshold:  distance - x * distance / threshold if x < threshold else 0
    ## TODO: other functions

def move_line(line,node,target,threshold,func = MOVE_FUNCTION.func_linear):
    if(node.distance(target) == 0):
        ## 同一个不需要移动
        return line
    SE_dis = node.distance(target)
    # 确定方向
    index = 0
    d0 = node.distance(shapely.geometry.Point(line.coords[0]))
    d1 = node.distance(shapely.geometry.Point(line.coords[-1]))
    if(d0 < d1):
        pass
    else:
        index = -1
        pois_temp = list(line.coords[0:])
        pois_temp.reverse()
        line = shapely.geometry.LineString(pois_temp)
    poi_array = np.array(line.coords[:])
    print(len(poi_array))
    for i in range(len(poi_array)):

        distance = line.project(shapely.geometry.Point(poi_array[i]))
        # 直线距离还是线段距离？
        move_dis = func(distance,SE_dis,threshold)
        new_poi = poi_array[i] + (np.array(target.coords) - np.array(node.coords)) * move_dis / target.distance(node)
        poi_array[i] = new_poi
    return shapely.geometry.LineString(poi_array)
        
def move_snap(line1,line2,threshold = 500,func = MOVE_FUNCTION.func_linear, default_persentage = 0.5):
    """snap line1 to line2, which means move the node in line1 to line2 and snap two lines together. line2 has no change in geometry.

    Args:
        line1 ([type]): [description]
        line2 ([type]): [description]
        threshold ([type]): [description]
        func ([type]): [description]
        default_persentage (): default persentage when the line is not long enough for the threshold
    """
    if line1.length < threshold:
        # 当线段长度不满足阈值时
        threshold = line1.length * default_persentage
    
    ## * 1. get start and target
    # method: closest node
    pois1 = np.array([line1.coords[0],line1.coords[-1]])
    pois2 = np.array([line2.coords[0],line2.coords[-1]])
    
    distance_matrix= np.sqrt((pois1[:,0].reshape(-1,1) - pois2[:,0].reshape(1,-1))** 2  + (pois1[:,1].reshape(-1,1) - pois2[:,1].reshape(1,-1))** 2)
    closeset_index = np.where(distance_matrix == np.min(distance_matrix)) #np.argmin(distance_matrix)
    
    start_node = shapely.geometry.Point(*pois1[closeset_index[0],:])
    end_node = shapely.geometry.Point(*pois2[closeset_index[1],:])
    
    return move_line(line1,start_node,end_node,threshold,func)


## TODO 捕捉到两个端点 未测试
def move_snap_both_end(line1,line2,threshold = 500,func = MOVE_FUNCTION.func_linear,default_persentage = 0.5):
    """snap line1 to line2, which means move the node in line1 to line2 and snap two lines together. line2 has no change in geometry.

    Args:
        line1 (shapely.geometry.LineString): 移动对象
        line2 (shapely.geometry.LineString): 目标对象
        threshold (float): 移动长度最大阈值
        func (MOVE_FUNCTION): 移动方法
        default_persentage(float): 当线段长度不满足最大阈值时，最大影响百分比
    """
    ## ! 0. determine whether the length of the line is greater than the threshold
    ## TODO 待测试 20210731 --- 跑了一次 有bug
    if line1.length < threshold:
        threshold = line1.length * default_persentage
        
    ## * 1. get start and target
    # method: closest node
    pois1 = np.array([line1.coords[0],line1.coords[-1]])
    pois2 = np.array([line2.coords[0],line2.coords[-1]])
    
    ## * 2. decide direction
    tag_distance_tag = shapely.geometry.Point(line1.coords[0]).distance(shapely.geometry.Point(line2.coords[0])) < shapely.geometry.Point(line1.coords[0]).distance(shapely.geometry.Point(line2.coords[-1]))
    if tag_distance_tag:
        # 说明是正向
        line1 = move_line(line1,shapely.geometry.Point(line1.coords[0]), shapely.geometry.Point(line2.coords[0]),threshold,func)
        line1 = move_line(line1,shapely.geometry.Point(line1.coords[-1]), shapely.geometry.Point(line2.coords[-1]),threshold,func)
    else:
        # 反向的
        line1 = move_line(line1,shapely.geometry.Point(line1.coords[0]), shapely.geometry.Point(line2.coords[-1]),threshold,func)
        line1 = move_line(line1,shapely.geometry.Point(line1.coords[-1]), shapely.geometry.Point(line2.coords[0]),threshold,func)
    
    return line1